/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Igor V. Stolyarov
 */
package com.jgraph.gaeawt.java.awt.image;

import java.util.Arrays;

public class AreaAveragingScaleFilter extends ReplicateScaleFilter
{

	private static final ColorModel rgbCM = ColorModel.getRGBdefault();

	private static final int averagingFlags = (ImageConsumer.TOPDOWNLEFTRIGHT | ImageConsumer.COMPLETESCANLINES);

	private boolean reset = true; // Flag for used superclass filter

	private boolean inited = false; // All data inited

	private int sum_r[]; // Array for average Red samples

	private int sum_g[]; // Array for average Green samples

	private int sum_b[]; // Array for average Blue samples

	private int sum_a[]; // Array for average Alpha samples

	private int buff[]; // Stride buffer

	private int avgFactor; // Global averaging factor

	private int cachedDY; // Cached number of the destination scanline 

	private int cachedDVRest; // Cached value of rest src scanlines for sum 
								// pixel samples 
								// Because data if transfering by whole scanlines
								// we are caching only Y coordinate values

	public AreaAveragingScaleFilter(int width, int height)
	{
		super(width, height);
	}

	@Override
	public void setPixels(int x, int y, int w, int h, ColorModel model,
			int[] pixels, int off, int scansize)
	{
		if (reset)
		{
			super.setPixels(x, y, w, h, model, pixels, off, scansize);
		}
		else
		{
			setFilteredPixels(x, y, w, h, model, pixels, off, scansize);
		}
	}

	@Override
	public void setHints(int hints)
	{
		super.setHints(hints);
		reset = ((hints & averagingFlags) != averagingFlags);
	}

	/**
	 * This method implemented Area Averaging Scale filter.
	 * The description of algorithm is presented in Java API Specification.
	 *  
	 * Arrays sum_r, sum_g, sum_b, sum_a have length equals width of destination 
	 * image. In each array's element is accumulating pixel's component values, 
	 * proportional to the area which source pixels will occupy in destination 
	 * image. Then that values will divide by Global averaging  
	 * factor (area of the destination image) for receiving  
	 * average values of destination pixels.
	 * 
	 * @param x - Src pixels X coordinate
	 * @param y - Src pixels Y coordinate
	 * @param w - width of the area of Src pixels
	 * @param h - height of the area of Src pixels
	 * @param model - Color Model of Src pixels
	 * @param pixels - array of Src pixels
	 * @param off - offset into the Src pixels array
	 * @param scansize - length of scanline in the pixels array
	 */
	private void setFilteredPixels(int x, int y, int w, int h,
			ColorModel model, int[] pixels, int off, int scansize)
	{
		if (!inited)
		{
			initialize();
		}

		int srcX, srcY, dx, dy;
		int svRest, dvRest, shRest, dhRest, vDif, hDif;

		if (y == 0)
		{
			dy = 0;
			dvRest = srcHeight;
		}
		else
		{
			dy = cachedDY;
			dvRest = cachedDVRest;
		}

		srcY = y;
		svRest = destHeight;

		int srcOff = off;
		while (srcY < y + h)
		{
			if (svRest < dvRest)
			{
				vDif = svRest;
			}
			else
			{
				vDif = dvRest;
			}

			srcX = 0;
			dx = 0;
			shRest = destWidth;
			dhRest = srcWidth;
			while (srcX < w)
			{
				if (shRest < dhRest)
				{
					hDif = shRest;
				}
				else
				{
					hDif = dhRest;
				}
				int avg = hDif * vDif; // calculation of contribution factor

				int rgb, pix;

				pix = pixels[srcOff + srcX];
				rgb = pix;
				int a = rgb >>> 24;
				int r = (rgb >> 16) & 0xff;
				int g = (rgb >> 8) & 0xff;
				int b = rgb & 0xff;

				// accumulating pixel's component values
				sum_a[dx] += a * avg;
				sum_r[dx] += r * avg;
				sum_g[dx] += g * avg;
				sum_b[dx] += b * avg;

				shRest -= hDif;
				dhRest -= hDif;

				if (shRest == 0)
				{
					srcX++;
					shRest = destWidth;
				}

				if (dhRest == 0)
				{
					dx++;
					dhRest = srcWidth;
				}
			}

			svRest -= vDif;
			dvRest -= vDif;

			if (svRest == 0)
			{
				svRest = destHeight;
				srcY++;
				srcOff += scansize;
			}

			if (dvRest == 0)
			{
				// averaging destination pixel's values
				for (int i = 0; i < destWidth; i++)
				{
					int a = (sum_a[i] / avgFactor) & 0xff;
					int r = (sum_r[i] / avgFactor) & 0xff;
					int g = (sum_g[i] / avgFactor) & 0xff;
					int b = (sum_b[i] / avgFactor) & 0xff;
					int frgb = (a << 24) | (r << 16) | (g << 8) | b;
					buff[i] = frgb;
				}
				consumer.setPixels(0, dy, destWidth, 1, rgbCM, buff, 0,
						destWidth);
				dy++;
				dvRest = srcHeight;
				Arrays.fill(sum_a, 0);
				Arrays.fill(sum_r, 0);
				Arrays.fill(sum_g, 0);
				Arrays.fill(sum_b, 0);
			}

		}

		cachedDY = dy;
		cachedDVRest = dvRest;

	}

	/**
	 * Initialization of the auxiliary data
	 */
	private void initialize()
	{

		sum_a = new int[destWidth];
		sum_r = new int[destWidth];
		sum_g = new int[destWidth];
		sum_b = new int[destWidth];

		buff = new int[destWidth];
		outpixbuf = buff;
		avgFactor = srcWidth * srcHeight;

		inited = true;
	}
}
